package com.huaxin.dss2.util;

import com.google.gson.JsonElement;
import com.huaxin.dss2.data.NewSmallStation.FBoxSignalRConnection;
import com.huaxin.dss2.data.NewSmallStation.fbox.LoggerFactory;
import com.huaxin.dss2.data.NewSmallStation.fbox.ServerCaller;
import com.huaxin.dss2.data.NewSmallStation.fbox.StaticCredentialProvider;
import com.huaxin.dss2.data.NewSmallStation.fbox.TokenManager;
import com.huaxin.dss2.data.bean.DataValue;
import com.huaxin.dss2.data.bean.properties.Fbox;
import com.huaxin.dss2.message.MessageCache;
import com.huaxin.dss2.util.Constant.FBoxState;
import java.io.IOException;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.apache.directory.api.util.Strings;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * SignalR工具类,用于处理各种
 * @author : luqing
 * @date : 2018/10/24 14:18
 */
@Slf4j
public class SignalRUtils {
    private static FBoxSignalRConnection fboxSignalR;
    private static ServerCaller commServer;
    private static ServerCaller appServer;
    private static LoggerFactory loggerFactory;
    private static TokenManager tokenManager;
    private static Fbox fbox;

    static {
        loggerFactory = new LoggerFactory();

        fbox = Constant.getProperties().getFbox();
        tokenManager = new TokenManager(
            new StaticCredentialProvider(
                fbox.getClientId(), fbox.getClientSecret(),
                fbox.getUsername(), fbox.getPassword()
            ),
            fbox.getIdServerUrl(), new LoggerFactory()
        );
    }

    /**
     * 更新signalRClientId操作
     * 根据反馈数据状态推送异常
     * 厂家回复结果为:signalR重连时需要将signalRClientId进行更新,否则会导致数据漏发操作
     */
    public static void updateServer(){

        //重置cid
        String signalRClientId = UUID.randomUUID().toString();
        log.info("更新cid操作,cid={}",signalRClientId);

        createServer(signalRClientId);
        //创建Api连接
        fboxSignalR = new FBoxSignalRConnection(fbox.getCommServerSignalRUrl(), signalRClientId, tokenManager, Constant.PROXY, loggerFactory);

    }

    /**
     * 创建连接服务
     * @param signalRClientId cid
     */
    public static void createServer(String signalRClientId){
        //读取box厂家历史记录服务器api 详见文档v1.2.2 名词解释
        commServer = new ServerCaller(tokenManager, fbox.getCommServerApiUrl(), signalRClientId, loggerFactory);
        //读取厂家boxRegs API 详见文档v1.2.2 4.1
        appServer = new ServerCaller(tokenManager, fbox.getAppServerApiUrl(), signalRClientId, loggerFactory);
    }

    /**
     * 开始fbox的监听
     */
    public static void startFBoxSignalR(){
        fboxSignalR.start();
    }

    /**
     * 对盒子进行开点操作
     * @param boxUid 盒子id
     */
    public static void startBox(Long boxUid){
        try {
            commServer.executePost("box/" + boxUid + "/dmon/start", String.class);
            // 获取所有站点的小数位 详见文档v1.2.2 5.2.1FBox监控点列表
            JsonElement jsonElement = commServer.executeGet("v2/box/" + boxUid + "/dmon/grouped", JsonElement.class);
            // 将响应内容进行处理
            SignalRUtils.cacheFracDigits(jsonElement);
            log.info("盒子{}开点结束",boxUid);
        } catch (IOException e) {
            log.info("盒子{}开点失败",boxUid,e);
        }

    }

    /**
     * 嘉靖处理iswarn的问题
     * 特殊处理方式
     */
    public static void changeIsWarnMap(){
        //获取所有需要保存的站点
        //遍历站点
        for (String stationCode : ScheduledValue.STATION_CODE_LIST){

            if (Strings.isEmpty(stationCode)){
                continue;
            }

            //通过站点生成对应的特殊指标
            String dataCode = stationCode+"_0_iswarn";
            DataValue dataValue = new DataValue();
            dataValue.setDataCode(dataCode);
            dataValue.setDataValue(0d);

            //将特殊指标存入map中
            ScheduledValue.DATA_IS_WARN_MAP.put(stationCode,dataValue);
        }
    }

    /**
     * 将小数位不为0的指标放入内存进行缓存，便于存值时处理
     * @update 2018/8/24 浮点型的小数位不需要进行计算
     * @param jsonElement 从fbox厂家接口获取的值 返回内容  详见文档v1.2.2 5.2.1FBox监控点列表
     */
    public static void cacheFracDigits(JsonElement jsonElement) {

        if (jsonElement == null) {
            return;
        }

        try {
            JSONArray jsonArray = new JSONArray(jsonElement.toString());
            //详见文档v1.2.2 5.2.1FBox监控点列表 响应内容
            JSONArray items = jsonArray.getJSONObject(0).getJSONArray("items");
            if (items == null) {
                return;
            }

            //遍历响应内容
            for (int i = 0; i < items.length(); i++) {
                JSONObject item = items.getJSONObject(i);
                int fracLength = item.getInt("fracDigits");
                //判断小数位的长度是否大于0
                if (0 < fracLength) {
                    //处理itemId与平台不一致问题
                    String name = item.getString("name");
                    String[] sp = name.split("_");

                    //截取三段，不是三段的数据为错误数据
                    //对于不同的采集平台，长度可能会不一样
                    if (sp.length != Constant.getFullCode().getFracDigitsLength()) {
                        continue;
                    }

                    //拼接新的name
                    name = SignalRUtils.getFullCode(sp);
                    // 并且判断是否是不属于浮点型
                    if(item.getInt("dataType") != 16){
                        //将对应指标的对应小数位长度存入valueFracDigitsMap中
                        ScheduledValue.VALUE_FRAC_DIGITS_MAP.put(name, fracLength);
                    }else {
                        ScheduledValue.FLOAT_FRAC_DIGITS_MAP.put(name,fracLength);
                    }
                }
            }
        } catch (JSONException e) {
            log.error("存值处理时发生异常",e);
        }
    }


    /**
     * 盒子状态改变时的操作
     * @param state 状态
     * @param boxUid 盒子id
     * @throws IOException 抛出I0异常
     */
    public static void boxStateChangedDispose(Integer state,String boxUid) throws IOException {
        FBoxState fBoxState = FBoxState.getFBoxState(state);
        switch (fBoxState){
            //状态变更为上线状态时处理方式
            case ON_LINE:
                log.info("开始处理盒子{}{}状态操作", boxUid, fBoxState.desc);
                // 开启盒子监听 详见文档v1.2.2 8.5开启FBox所有监控点
                startBox(Long.parseLong(boxUid));
                break;
            //状态变更为掉线和超时时处理方式
            case TIME_OUT:
            case LOST_CONNECTION:
                log.info("开始处理盒子{}{}状态操作", boxUid, fBoxState.desc);
                //调用获取站点信息 详见文档v1.2.2 5.2.1FBox监控点列表
                JsonElement jsonElement = commServer.executeGet("v2/box/" + boxUid + "/dmon/grouped",JsonElement.class);
                //移除内存和redis中的掉线站点
                removeLostConnDataMapCache(jsonElement);
                break;
            default:
                log.info("盒子{}{}状态暂未进行出处理",boxUid,fBoxState.desc);
        }
    }

    /**
     * 从内存和redis中移除掉线站点的值
     * @param jsonElement 从fbox厂家接口获取的值 返回内容  详见文档v1.2.2 5.2.1FBox监控点列表
     */
    private static void removeLostConnDataMapCache(JsonElement jsonElement) {

        if (jsonElement == null) {
            return;
        }

        try {
            JSONArray jsonArray = new JSONArray(jsonElement.toString());
            JSONArray items = jsonArray.getJSONObject(0).getJSONArray("items");
            if (items == null) {
                return;
            }
            for (int i = 0; i < items.length(); i++) {
                JSONObject item = items.getJSONObject(i);
                String name = item.getString("name");
                String[] sp = name.split("_");

                //截取三段，不是三段的数据为错误数据
                //对于不同的采集平台，长度可能会不一样
                if (sp.length != Constant.getFullCode().getFracDigitsLength()) {
                    continue;
                }
                //拼接新的name
                name = SignalRUtils.getFullCode(sp);

                // 从redis中移除站点key
                MessageCache.stationStaticOps.delete(sp[Constant.getFullCode().getStationIndex()]);
                // 从内存中移除相应指标
                ScheduledValue.DATA_VALUE_MAP.remove(name);
            }
        } catch (JSONException e) {
            log.error("移除redis异常",e);
        }
    }


    /**
     * 获取三段编码
     * 两种情况
     * 1 使用dataItemId : stationCode_dataItemId_dataCode
     * 2 不用dataItemId ：stationCode_1_dataCode
     * @param sp 三段数组 sp[0]:stationCode,sp[1]:dataItemId,sp[2]:dataCode
     * @return 三段编码
     */
    public static String getFullCode(String[] sp){
        String fullCode =  sp[Constant.getFullCode().getStationIndex()] + "_1_" + sp[Constant.getFullCode().getDataCodeIndex()];
        if (Constant.getProperties().getUseDataItemId()){
            fullCode = sp[Constant.getFullCode().getStationIndex()] + "_" + sp[Constant.getFullCode().getDataItemIndex()] + "_" + sp[Constant.getFullCode().getDataCodeIndex()];
        }
        return fullCode;
    }

}
